/*
 * Copyright (c) 2020-2021, SERI Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date             Author      Notes
 * 2023-12-23       Lyons       first version
 */

`ifdef TESTBENCH_VCS
`include "pa_chip_param.v"
`else
`include "../pa_chip_param.v"
`endif

module pa_perips_gpio (
    input  wire                         clk_i,
    input  wire                         rst_n_i,

    input  wire [7:0]                   addr_i,
    input  wire                         data_rd_i,
    input  wire                         data_we_i,
    input  wire [`DATA_BUS_WIDTH-1:0]   data_i,
    output wire [`DATA_BUS_WIDTH-1:0]   data_o,

    inout  wire                         pad_PA0,
    inout  wire                         pad_PA1,
    inout  wire                         pad_PA2,
    inout  wire                         pad_PA3,
    inout  wire                         pad_PA4,
    inout  wire                         pad_PA5,
    inout  wire                         pad_PA6,
    inout  wire                         pad_PA7,
    inout  wire                         pad_PA8,
    inout  wire                         pad_PA9,
    inout  wire                         pad_PA10,
    inout  wire                         pad_PA11,
    inout  wire                         pad_PA12,
    inout  wire                         pad_PA13,
    inout  wire                         pad_PA14,
    inout  wire                         pad_PA15
);


localparam GPIO_REG_CR                  = 8'h00;
localparam GPIO_REG_IN                  = 8'h04;
localparam GPIO_REG_OUT                 = 8'h08;
localparam GPIO_REG_RSVD                = 8'h0c;
localparam GPIO_REG_OBIT                = 8'h10;


// [15:0] : direction, 0:out, 1:in
reg  [`DATA_BUS_WIDTH-1:0]              _gpio_cr;

// [15:0] : input bit
reg  [`DATA_BUS_WIDTH-1:0]              _gpio_in;

// [15:0] : output bit
reg  [`DATA_BUS_WIDTH-1:0]              _gpio_out;


always @ (posedge clk_i or negedge rst_n_i) begin
    if (!rst_n_i) begin
        _gpio_cr  <= 32'h0000_4400;
        _gpio_out <= 32'h0000_fff7;
    end
    else begin
        if (data_we_i) begin
        case (addr_i[7:0])
            GPIO_REG_CR          : _gpio_cr      <= {16'b0, data_i[15:0]};
            GPIO_REG_OUT         : _gpio_out     <= {16'b0, data_i[15:0]};
            GPIO_REG_OBIT + 4*0  : _gpio_out[0 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*1  : _gpio_out[1 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*2  : _gpio_out[2 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*3  : _gpio_out[3 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*4  : _gpio_out[4 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*5  : _gpio_out[5 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*6  : _gpio_out[6 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*7  : _gpio_out[7 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*8  : _gpio_out[8 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*9  : _gpio_out[9 ] <=  data_i[0];
            GPIO_REG_OBIT + 4*10 : _gpio_out[10] <=  data_i[0];
            GPIO_REG_OBIT + 4*11 : _gpio_out[11] <=  data_i[0];
            GPIO_REG_OBIT + 4*12 : _gpio_out[12] <=  data_i[0];
            GPIO_REG_OBIT + 4*13 : _gpio_out[13] <=  data_i[0];
            GPIO_REG_OBIT + 4*14 : _gpio_out[14] <=  data_i[0];
            GPIO_REG_OBIT + 4*15 : _gpio_out[15] <=  data_i[0];
        endcase
        end
    end
end

reg  [`DATA_BUS_WIDTH-1:0]              _data = `ZERO_WORD;

always @ (posedge clk_i) begin
    if (data_rd_i) begin
    case (addr_i[7:0])
        GPIO_REG_CR : _data <= _gpio_cr;
        GPIO_REG_IN : _data <= _gpio_in;
        default     : _data <= `ZERO_WORD;
    endcase
    end
end

wire [`DATA_BUS_WIDTH-1:0]              pad_PIn;

IOBUF uut_0 (
    .O (pad_PIn[0]),
    .IO (pad_PA0),
    .I (_gpio_out[0]),
    .OEN (_gpio_cr[0])
);

IOBUF uut_1 (
    .O (pad_PIn[1]),
    .IO (pad_PA1),
    .I (_gpio_out[1]),
    .OEN (_gpio_cr[1])
);

IOBUF uut_2 (
    .O (pad_PIn[2]),
    .IO (pad_PA2),
    .I (_gpio_out[2]),
    .OEN (_gpio_cr[2])
);

IOBUF uut_3 (
    .O (pad_PIn[3]),
    .IO (pad_PA3),
    .I (_gpio_out[3]),
    .OEN (_gpio_cr[3])
);

IOBUF uut_4 (
    .O (pad_PIn[4]),
    .IO (pad_PA4),
    .I (_gpio_out[4]),
    .OEN (_gpio_cr[4])
);

IOBUF uut_5 (
    .O (pad_PIn[5]),
    .IO (pad_PA5),
    .I (_gpio_out[5]),
    .OEN (_gpio_cr[5])
);

IOBUF uut_6 (
    .O (pad_PIn[6]),
    .IO (pad_PA6),
    .I (_gpio_out[6]),
    .OEN (_gpio_cr[6])
);

IOBUF uut_7 (
    .O (pad_PIn[7]),
    .IO (pad_PA7),
    .I (_gpio_out[7]),
    .OEN (_gpio_cr[7])
);

IOBUF uut_8 (
    .O (pad_PIn[8]),
    .IO (pad_PA8),
    .I (_gpio_out[8]),
    .OEN (_gpio_cr[8])
);

IOBUF uut_9 (
    .O (pad_PIn[9]),
    .IO (pad_PA9),
    .I (_gpio_out[9]),
    .OEN (_gpio_cr[9])
);

IOBUF uut_10 (
    .O (pad_PIn[10]),
    .IO (pad_PA10),
    .I (_gpio_out[10]),
    .OEN (_gpio_cr[10])
);

IOBUF uut_11 (
    .O (pad_PIn[11]),
    .IO (pad_PA11),
    .I (_gpio_out[11]),
    .OEN (_gpio_cr[11])
);

IOBUF uut_12 (
    .O (pad_PIn[12]),
    .IO (pad_PA12),
    .I (_gpio_out[12]),
    .OEN (_gpio_cr[12])
);

IOBUF uut_13 (
    .O (pad_PIn[13]),
    .IO (pad_PA13),
    .I (_gpio_out[13]),
    .OEN (_gpio_cr[13])
);

// IOBUF uut_14 (
    // .O (pad_PIn[14]),
    // .IO (pad_PA14),
    // .I (_gpio_out[14]),
    // .OEN (_gpio_cr[14])
// );

// IOBUF uut_15 (
    // .O (pad_PIn[15]),
    // .IO (pad_PA15),
    // .I (_gpio_out[15]),
    // .OEN (_gpio_cr[15])
// );

assign pad_PIn[31:14] = 18'b0;

always @ (posedge clk_i) begin
    if (!rst_n_i) begin
        _gpio_in <= `ZERO_WORD;
    end
    else begin
        _gpio_in <= pad_PIn;
    end
end

assign data_o[`DATA_BUS_WIDTH-1:0] = _data[`DATA_BUS_WIDTH-1:0];

endmodule
